MIT6.s081实验记录(一):gdb调试qemu方法 & lab1 |
您所在的位置:网站首页 › qemu gdb教程 › MIT6.s081实验记录(一):gdb调试qemu方法 & lab1 |
MIT6.s081实验记录(一):gdb调试qemu方法 & lab1
准备工作
xv6调试
在xv6文件夹下make qemu-gdb启动qemu上的gdbserver。 xv6的ISA是riscv,所以我们需要使用riscv的调试器riscv64-unknown-elf-gdb来调试xv6。 使用tmux分屏,在另一窗口中,在相同的xv6文件夹下riscv64-unknown-elf-gdb 再加上要调试的文件名(比如kernel/kernel或者user/_sleep),也可以进入gdb后再输入file user/_[execname]加载可执行文件。 可以看到riscv的gdb跑起来了,输入: (gdb) target remote localhost:26000与qemu中的gdbserver建立连接,不然会提醒你the program is not being run. 现在可以看到以下输出 Remote debugging using localhost:26000 0x0000000000001000 in ?? ()每次都要输入target remote localhost:26000很麻烦,根据提示 ![]() 我们在用户目录下的.gdbinit文件中添加以下内容即可: add-auto-load-safe-path /home/bolee/s081/xv6-labs-2020/.gdbinit通过b [linenum]设置断点。gdb在user程序中打断点会出现Cannot access memory at address错误,需要自己在.gdbinit.tmpl-riscv里加一行set riscv use-compressed-breakpoints yes。 设置了断点后再输入c继续,此时gdb窗口会卡住,等待qemu中的相关程序执行 ![]() 在make qemu-gdb的终端中执行该可执行文件,此时才可以在gdb窗口中开始debug ![]() 在编写好程序之后,需要在makefile中把我们写好的sleep.c加进去: UPROGS=\ $U/_cat\ $U/_echo\ $U/_forktest\ ........ $U/_kalloctest\ $U/_bcachetest\ $U/_alloctest\ $U/_bigfile\ $U/_sleep\make qemu就会将我们加入的程序编译,sleep.c就会被编译成可执行文件_sleep,并保存在xv6的文件系统中,然后就可以在xv6 shell中运行我们写好的程序。 我们可以在user目录下看到我们编译生成的可执行文件, ![]() 这些可执行文件的格式为riscv,无法在本地x86的机器上执行,只能在qemu中模拟的riscv环境上运行 ![]() 运行make grade可以测试我们的程序是否正确。make grade会运行所有的程序,如果只是想针对某一个assignment运行测试,运行 $ ./grade-lab-util sleep或者 $ make GRADEFLAGS=sleep grade以上会针对sleep程序进行测试。 sleepImplement the UNIX program sleep for xv6; your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. 在kernel/sysproc.c 中查看System系统调用的实现,在user/user.h中查看可从用户程序调用的sleep的 C 定义,在user/usys.s中查看从用户代码跳转到内核以进行sleep的汇编代码。 可以使用atoi将传入的字符串转为integer,该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。 #include "../kernel/types.h" #include "user.h" int main(int argc, char *argv[]) { if(argc != 2){ // 向标准错误中输出错误信息 fprintf(2,"you must input sleep time"); exit(1); } // atoi将字符串转为int,再进行系统调用sleep sleep(atoi(argv[1])); exit(0); } pingpong编写一个程序,使用 UNIX 系统调用在两个进程之间通过一对管道“ping-pong”一个字节,每个管道一个。 父进程应该向子进程发送一个字节; 子进程应该打印“: received ping”,其中 是它的进程 ID,将管道上的字节写入父进程,然后退出; 父进程应该从子进程那里读取字节,打印“: received pong”,然后退出。我们的解决方案应该在文件 user/pingpong.c 中。 #include "../kernel/types.h" #include "user.h" #define READ_END 0 #define WRITE_END 1 int main(int argc,char *argv[]) { int p_child[2]; int p_parent[2]; char buf[1]; pipe(p_child); pipe(p_parent); // 子进程 if(fork() == 0){ close(p_child[READ_END]); close(p_parent[WRITE_END]); read(p_parent[READ_END],buf,1); printf("%d: received ping\n", getpid()); write(p_child[WRITE_END]," ",1); close(p_child[WRITE_END]); close(p_parent[READ_END]); exit(0); }else{ // 关闭父进程接收管道的写入端描述符 close(p_child[WRITE_END]); // 关闭父进程输出管道的接收端描述符 close(p_parent[READ_END]); write(p_parent[WRITE_END]," ",1); read(p_child[READ_END],buf,1); printf("%d: received pong\n", getpid()); close(p_child[READ_END]); close(p_parent[WRITE_END]); exit(0); } } primes使用管道编写并发版本的素数筛 Your goal is to use pipe and fork to set up the pipeline. The first process feeds the numbers 2 through 35 into the pipeline. For each prime number, you will arrange to create one process that reads from its left neighbor over a pipe and writes to its right neighbor over another pipe. ![]() |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |